using System;
using System.Diagnostics;
using System.Collections.Generic;
using System.Globalization;

namespace Dida
{
	public interface IAniObject
	{
		void Start();
		void Update(float timeDelta);
	}

	public interface IAniValue<T> : IAniObject
	{
		T CurrentValue
		{
			get;
		}
	}

	public struct AniSocket<T>
	{
		IAniValue<T> animatedValue;
		T nonAnimatedValue;

		public AniSocket(T nonAnimatedValue)
		{
			animatedValue = null;
			this.nonAnimatedValue = nonAnimatedValue;
		}

		public AniSocket(IAniValue<T> animatedValue)
		{
			this.animatedValue = animatedValue;
			nonAnimatedValue = default(T);
		}

		public void SetValue(T value)
		{
			animatedValue = null;
			nonAnimatedValue = value;
		}

		public void SetAnimatedValue(IAniValue<T> animatedValue)
		{
			this.animatedValue = animatedValue;
		}

		public T Value
		{
			get
			{
				if (animatedValue == null)
					return nonAnimatedValue;
				else
					return animatedValue.CurrentValue;
			}
		}
	}

	public abstract class AniSocketParser<T> : Dxml.IComplexParser
	{
		XmlPositionMarker errorPos;
		bool isPlugged;
		T nonPluggedVal;
		string refName;
				
		public void Load(string str, XmlPositionMarker errorPos)
		{
			this.errorPos = errorPos;

			str = str.Trim();
			if(str.Length == 0)
				throw new XmlReaderException("Invalid value",errorPos);

			if (str[0] == '#')
			{
				isPlugged = true;
				refName = str.Substring(1);
			}
			else
			{
				try
				{
					isPlugged = false;
					nonPluggedVal = ParseNonPlugged(str);
				}
				catch (Exception err)
				{
					throw new XmlReaderException(err, errorPos);
				}
			}
		}

		protected abstract T ParseNonPlugged(string str);

		public Dida.Dxml.CompilingReference[] GetReferences()
		{
			if (isPlugged)
			{
				return new Dxml.CompilingReference[]
				{
					new Dxml.CompilingReference(refName,typeof(IAniValue<T>),errorPos),
				};
			}
			else
				return new Dxml.CompilingReference[0];
		}

		public object GetObject(object[] references)
		{
			if (isPlugged)
				return new AniSocket<T>((IAniValue<T>)references[0]);
			else
				return new AniSocket<T>(nonPluggedVal);
		}
	}

	public class FloatSocketParser : AniSocketParser<float>
	{
		protected override float ParseNonPlugged(string str)
		{
			return float.Parse(str, NumberStyles.Float);
		}
	}

	public class Vector3DSocketParser : AniSocketParser<Vector3D>
	{
		protected override Vector3D ParseNonPlugged(string str)
		{
			return Vector3D.Parse(str);
		}
	}

	public class EulerRotSocketParser : AniSocketParser<EulerRot>
	{
		protected override EulerRot ParseNonPlugged(string str)
		{
			return EulerRot.Parse(str);
		}
	}

	public class QuaternionSocketParser : AniSocketParser<Quaternion>
	{
		protected override Quaternion ParseNonPlugged(string str)
		{
			return Quaternion.Parse(str);
		}
	}

	public class SimpleTransformSocketParser : AniSocketParser<SimpleTransform>
	{
		protected override SimpleTransform ParseNonPlugged(string str)
		{
			str = str.Trim();
			if (str == "IDENTITY")
			{
				return new SimpleTransform(new Vector3D(0, 0, 0), Quaternion.Identity);
			}
			else
				throw new Exception("Invalid transform: \"" + str + "\".");
		}
	}
}